/**
	Pulsar engine. Core.
	Memory managment (debug versions)

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

#define __PL_NO_MEMORY_MAPPING__

#include <pulsar/pulsar.h>
#include <pulsar/src/pulsar_intr.h>

#if defined(PL_MEMORY_DEBUG)

namespace pl
{

const pl_uint32 _HEADER_SIGN = PL_FOURCC('R', 'K' ,'M' ,'N');
const pl_char _FOOTER_SIGN = PL_T('\x5A');
const pl_char _FREE_SIGN = PL_T('\xFA');

static size_t _calcDebugSpace(size_t size, pl_uint32 align);
static void* _getUserAddress(void* pAllocatedAddress, pl_uint32 align);
static void _fillDebugInfo(void* pAllocatedAddress, void* pUserAddress, size_t blockSize, const pl_char* fileName, pl_uint32 lineNumber, const pl_char* func, BlockType blockType);
static void _linkBlock(void* pUserAddress);
static void _unlinkBlock(void* pUserAddress);

} // namespace pl

/**
*/
bool pl::memDebugMemoryWalk(BlockInfo &info)
{
	_MemHeader* pHead;

	if (info.p == NULL)
	{
		// First block in list
		pHead = _memGlobalInfo.pFirstBlock;
	}
	else
	{
		pHead = reinterpret_cast<_MemHeader*>(info.p) - 1;
		pHead = pHead->pNextBlock;
	}
	if (pHead == NULL)
	{
		return false;
	}

	size_t size;

	info.p = pHead+1;
	info.pAllocated = _getAllocAddress(info.p, size);
	info.size = pHead->blockSize;
	info.fileName = pHead->fileName;
	info.funcName = pHead->funcName;
	info.lineNumber = pHead->lineNumber;
	info.usageFlag = 1;
	info.blockStatus = _checkBlock(info.p, -1, 0);
	info.allocType = (BlockType)(pHead->blockType);
	info.allocationIndex = pHead->allocationIndex;
	info.threadId = pHead->threadId;
	return true;
}

/**
*/
bool pl::memDebugMemoryStatus(MemoryStat &status, bool bCheckErrors)
{
	bool rc = true;

	status.nAllocCurrentBlocks = _memGlobalInfo.nAllocatedBlocks;
	status.nAllocSumBlocks = _memGlobalInfo.curAllocationIndex;
	status.nAllocSize = 0;
	status.nSumAllocSize = 0;
	status.nFreeBlocks = (pl_uint32)-1;
	status.allocatedPtrMin = NULL;
	status.allocatedPtrMax = NULL;

	_memGlobalInfo.pDebugCs->enter();

	_MemHeader* pHead = _memGlobalInfo.pFirstBlock;

	while (pHead)
	{
		status.nAllocCurrentBlocks ++;
		status.nAllocSumBlocks ++;
		status.nAllocSize += pHead->blockSize;

		if (bCheckErrors)
		{
			if (_checkBlock(pHead+1, -1, 0) != MEMORY_ERROR_NONE)
			{
				rc = false;
			}
		}

		if (pHead == _memGlobalInfo.pFirstBlock)
		{
			status.allocatedPtrMin = (pHead+1);
			status.allocatedPtrMax = (void*)((pl_uint8*)status.allocatedPtrMin + pHead->blockSize);
		}
		else
		{
			status.allocatedPtrMin = min<void*>(status.allocatedPtrMin, (void*)(pHead+1));
			status.allocatedPtrMax = max<void*>(status.allocatedPtrMax, (void*)((pl_uint8*)status.allocatedPtrMin + pHead->blockSize));
		}
		pHead = pHead->pNextBlock;
	}
	_memGlobalInfo.pDebugCs->leave();
	return rc;
}

/**
*/
bool pl::memDebugMemoryDump(const Filepath& fileName, bool skipCoreAllocations /*= false*/)
{
	_memGlobalInfo.fInternalAllocations = true;
	pl::StreamWrite* stream = pl::FileManager::openFileWrite(fileName, pl::OPEN_FILE_SHARE_READ | pl::OPEN_FILE_CREATEALWAYS);
	_memGlobalInfo.fInternalAllocations = false;

	if (stream == NULL)
	{
		return false;
	}

#if defined(PL_UNICODE)
#if defined(PL_BYTE_BE)
	pl_uint16 magic = 0XFEFF;
	stream->write(magic);
#elif defined (PL_BYTE_LE)
	pl_uint16 magic = 0XFEFF;
	stream->write(magic);
#else
#error "unknown platform"
#endif
#endif

	bool rc = memDebugMemoryDump(stream, skipCoreAllocations);
	PL_DELETE stream;
	return rc;
}

/**
*/
bool pl::memDebugMemoryDump(pl::StreamWrite* s, bool skipCoreAllocations /*= false*/)
{
	_memGlobalInfo.pDebugCs->enter();
	_MemHeader* pHead = _memGlobalInfo.pFirstBlock;

	while (pHead != NULL)
	{
		if (skipCoreAllocations && pHead->internalAllocationFlag)
		{
			// Skip these blocks
		}
		else
		{
			String<1024> str;
			_formatString(str, pHead);
			str << PL_T("\r\n");
			s->writeBuffer(str.get(), sizeof(pl_char), str.size());
		}
		pHead = pHead->pNextBlock;
	}
	_memGlobalInfo.pDebugCs->leave();
	return true;
}

/**
*/
void pl::memDebugMemoryLog(bool skipCoreAllocations /*= false*/)
{
	_memGlobalInfo.pDebugCs->enter();
	_MemHeader* pHead = _memGlobalInfo.pFirstBlock;

	while (pHead != NULL)
	{
		if (skipCoreAllocations && pHead->internalAllocationFlag)
		{
			// Skip these blocks
		}
		else
		{
			String<1024> str;
			_formatString(str, pHead, false);
			plLogExtInfo(pHead->fileName ? pHead->fileName : PL_T("<NULL>"), pHead->lineNumber, str);
		}
		pHead = pHead->pNextBlock;
	}
	_memGlobalInfo.pDebugCs->leave();
	return;
}

/**
*/
void pl::memDebugMemoryTrace(bool skipCoreAllocations /*= false*/)
{
	_memGlobalInfo.pDebugCs->enter();
	_MemHeader* pHead = _memGlobalInfo.pFirstBlock;

	while (pHead != NULL)
	{
		if (skipCoreAllocations && pHead->internalAllocationFlag)
		{
			// Skip core allocation blocks
		}
		else
		{
			String<1024> str;
			_formatString(str, pHead, false);
			pl::_callOnTrace(pHead->fileName ? pHead->fileName : PL_T("<NULL>"), pHead->lineNumber, str.get());
		}
		pHead = pHead->pNextBlock;
	}
	_memGlobalInfo.pDebugCs->leave();
	return;
}

/**
*/
pl::MemoryError pl::memDebugCheckBlock(const void* p)
{
	if (p == NULL)
	{
		return MEMORY_ERROR_NULL;
	}
	return _checkBlock(p, -1, 0);
}

/**
*/
void pl::memDebugSetAllocBreakIndex(pl_uint32 index)
{
	_memGlobalInfo.breakIndex.pushBack(index);
	return;
}

/**
*/
void pl::memDebugSetAllocBreakAddress(const void* address)
{
	_memGlobalInfo.breakAddress.pushBack(address);
	return;
}


/**
*/
void pl::memDebugEnableMallocs(bool bEnable)
{
	_memGlobalInfo.fDisableMalloc = !bEnable;
	return;
}

void pl::memDebugEnableThreadCheck(bool bEnable)
{
	_memGlobalInfo.allocationOptions &= ~MEMOPTIONS_THREADS_MASK;
	_memGlobalInfo.allocationOptions |= (bEnable ? MEMOPTIONS_THREADS_DISABLE : MEMOPTIONS_THREADS_ENABLE);
	return;
}

/**
*/
void* pl::mallocDbg(size_t size, BlockType type, int align, const pl_char* file, pl_uint32 line, const pl_char* func)
{
	if (_memGlobalInfo.fDisableMalloc)
	{
		PL_MEM_ERROR1(file, line, func, NULL, size, MEMORY_ERROR_ALLOC_DISABLED);
		return NULL;
	}

	if (_memGlobalInfo.nMaxAllowedBlocks != 0 && _memGlobalInfo.nMaxAllowedBlocks < _memGlobalInfo.nAllocatedBlocks)
	{
		PL_MEM_ERROR1(file, line, func, NULL, size, MEMORY_ERROR_OUT);
		return NULL;
	}
	if (_memGlobalInfo.nMaxAllowedBytes != 0 && _memGlobalInfo.nMaxAllowedBytes < _memGlobalInfo.nAllocatedSize)
	{
		PL_MEM_ERROR1(file, line, func, NULL, size, MEMORY_ERROR_OUT);
		return NULL;
	}

	void* pRealAddress;
	if (_memGlobalInfo.iGlobalAlloc == NULL)
	{
		pRealAddress = malloc(_calcDebugSpace(size, align));
	}
	else
	{
		pRealAddress = _memGlobalInfo.iGlobalAlloc->onAlloc(_calcDebugSpace(size, align), align);
	}

	if (pRealAddress == NULL)
	{
		PL_MEM_ERROR1(file, line, func, NULL, size, MEMORY_ERROR_OUT);
		return NULL;
	}

	void* pUserAddress = _getUserAddress(pRealAddress, align);

	pl::IMemoryMonitor::_callOnAlloc(pUserAddress, size, file, line, func, type);
	_fillDebugInfo(pRealAddress, pUserAddress, size, file, line, func, type);
	_linkBlock(pUserAddress);
	return pUserAddress;
}

/*
*/
void* pl::callocDbg(size_t n, size_t size, const pl_char* file, pl_uint32 line, const pl_char* func)
{
	size_t totalSize = n*size;

	void* p = mallocDbg(totalSize, BLOCK_TYPE_MALLOC, 0, file, line, func);
	if (p == NULL)
	{
		PL_MEM_ERROR1(file, line, func, NULL, totalSize, MEMORY_ERROR_OUT);
		return NULL;
	}

	if (totalSize > 0)
	{
		memset(p, 0, totalSize);
	}
	return p;
}

/*
*/
void* pl::reallocDbg(void* p, size_t size, const pl_char* file, pl_uint32 line, const pl_char* func)
{
	pl_intptr threadId = 0;

	if (_memGlobalInfo.fEnableThreadCheck)
	{
		threadId = pl::getThreadId();
	}

	if (size == 0)
	{
		freeDbg(p, BLOCK_TYPE_MALLOC, file, line, func);
		return NULL;
	}
	if (p == NULL)
	{
		return mallocDbg(size, BLOCK_TYPE_MALLOC, 0, file, line, func);
	}

	MemoryError errCode = _checkBlock(p, BLOCK_TYPE_MALLOC, threadId);

	if (errCode != MEMORY_ERROR_NONE)
	{
		PL_MEM_ERROR1(file, line, func, p, size, errCode);
	}

	size_t sizeTmp;
	void* pAllocatedAddress = _getAllocAddress(p, sizeTmp);
	_unlinkBlock(p);

	void* pNewAllocatedAddress;
	if (_memGlobalInfo.iGlobalAlloc == NULL)
	{
		pNewAllocatedAddress = realloc(pAllocatedAddress, _calcDebugSpace(size, 0));
	}
	else
	{
		pNewAllocatedAddress = _memGlobalInfo.iGlobalAlloc->onRealloc(pAllocatedAddress, _calcDebugSpace(size, 0));
	}

	if (pNewAllocatedAddress == NULL)
	{
		_linkBlock(p);
		PL_MEM_ERROR1(file, line, func, NULL, size, MEMORY_ERROR_OUT);
		return NULL;
	}

	void* pNew = _getUserAddress(pNewAllocatedAddress, 0);

	pl::IMemoryMonitor::_callOnFree(p, sizeTmp, file, line, func, BLOCK_TYPE_MALLOC);
	pl::IMemoryMonitor::_callOnAlloc(pNew, size, file, line, func, BLOCK_TYPE_MALLOC);

	_fillDebugInfo(pNewAllocatedAddress, pNew, size, file, line, func, BLOCK_TYPE_MALLOC);
	_linkBlock(pNew);
	return pNew;
}

/*
*/
void pl::freeDbg(void* p, BlockType type, const pl_char* file, pl_uint32 line, const pl_char* func)
{
	if (p == NULL)
	{
		return;
	}

	pl_intptr threadId = 0;

	if (_memGlobalInfo.fEnableThreadCheck)
	{
		threadId = pl::getThreadId();
	}

	MemoryError errCode = _checkBlock(p, type, threadId);

	if (errCode != MEMORY_ERROR_NONE)
	{
		_replaceInfo(p, file, line, func);
		PL_MEM_ERROR1(file, line, func, p, 0, errCode);
	}


	size_t size;
	void* pAllocatedAddress = _getAllocAddress(p, size);
	_unlinkBlock(p);

	pl::IMemoryMonitor::_callOnFree(p, size, file, line, NULL, type);

	memset(pAllocatedAddress, _FREE_SIGN, _calcDebugSpace(size, 0));
	if (_memGlobalInfo.iGlobalAlloc == NULL)
	{
		free(pAllocatedAddress);
	}
	else
	{
		_memGlobalInfo.iGlobalAlloc->onFree(pAllocatedAddress);
	}
	return;
}

/*
*/
char* pl::strdupDbg(const char* str, const pl_char* file, pl_uint32 line, const pl_char* func)
{
	if (str == NULL)
	{
		PL_ASSERTW(false, PL_T("Wrong source String"));
		return NULL;
	}
	size_t size = strlen(str)+1;
	char* p = (char*)mallocDbg(size, BLOCK_TYPE_MALLOC, 0, file, line, func);
	if (p == NULL)
	{
		return NULL;
	}
	memcpy(p, str, size);
	return p;
}

/**
*/
void* pl::mallocStaticDbg(size_t size, const pl_char* file, pl_uint32 line, const pl_char* func)
{
	if (_memGlobalInfo.fDisableMalloc)
	{
		PL_MEM_ERROR1(file, line, func, NULL, size, MEMORY_ERROR_ALLOC_DISABLED);
		return NULL;
	}

	if (_memGlobalInfo.nMaxAllowedBlocks != 0 && _memGlobalInfo.nMaxAllowedBlocks < _memGlobalInfo.nAllocatedBlocks)
	{
		PL_MEM_ERROR1(file, line, func, NULL, size, MEMORY_ERROR_OUT);
		return NULL;
	}
	if (_memGlobalInfo.nMaxAllowedBytes != 0 && _memGlobalInfo.nMaxAllowedBytes < _memGlobalInfo.nAllocatedSize)
	{
		PL_MEM_ERROR1(file, line, func, NULL, size, MEMORY_ERROR_OUT);
		return NULL;
	}

	void* pRealAddress = mallocStaticRtl(_calcDebugSpace(size, _memGlobalInfo.staticAllocAlignment));

	if (pRealAddress == NULL)
	{
		PL_MEM_ERROR1(file, line, func, NULL, size, MEMORY_ERROR_OUT);
		return NULL;
	}
	void* pUserAddress = _getUserAddress(pRealAddress, _memGlobalInfo.staticAllocAlignment);

	_fillDebugInfo(pRealAddress, pUserAddress, size, file, line, func, BLOCK_TYPE_STATIC);
	_linkBlock(pUserAddress);
	return pUserAddress;
}

/**
*/
void pl::freeStaticDbg(void* p, const pl_char* file, pl_uint32 line, const pl_char* func)
{
	if (p == NULL)
	{
		return;
	}

	pl_intptr threadId = 0;

	if (_memGlobalInfo.fEnableThreadCheck)
	{
		threadId = pl::getThreadId();
	}

	MemoryError errCode = _checkBlock(p, BLOCK_TYPE_STATIC, threadId);

	if (errCode != MEMORY_ERROR_NONE)
	{
		_replaceInfo(p, file, line, func);
		PL_MEM_ERROR1(file, line, func, p, 0, errCode);
	}

	size_t size;
	void* pAllocatedAddress = _getAllocAddress(p, size);
	_unlinkBlock(p);

	memset(pAllocatedAddress, _FREE_SIGN, _calcDebugSpace(size, 0));
	freeStaticRtl(pAllocatedAddress);
	return;
}

/**
*/
pl::MemoryError pl::_checkBlock(const void* pUserAddress, int blockType, pl_intptr threadId)
{
	if (pUserAddress == NULL)
	{
		return MEMORY_ERROR_GENERAL;
	}
	const _MemHeader* pMemHeader = reinterpret_cast<const _MemHeader*>(pUserAddress) - 1;

	if (pMemHeader->sign1 != _HEADER_SIGN || pMemHeader->sign2 != _HEADER_SIGN)
	{
		return MEMORY_ERROR_HEADER;
	}
	if (pMemHeader->allocBeforeInit)
	{
		return MEMORY_ERROR_BEFORE_INIT;
	}

	for (pl_uint32 i=0; i<_memGlobalInfo.footerSize; i++)
	{
		if (((pl_uint8*)pUserAddress)[pMemHeader->blockSize+i] != _FOOTER_SIGN)
		{
			return MEMORY_ERROR_FOOTER;
		}
	}

	if (threadId != 0 && pMemHeader->threadId != 0 && threadId != 0 && pMemHeader->threadId != threadId)
	{
		return MEMORY_ERROR_MIX_THREAD;
	}

	if (blockType != -1 && blockType != pMemHeader->blockType)
	{
		switch (blockType)
		{
			case BLOCK_TYPE_MALLOC:
				return MEMORY_ERROR_MIX_FREE;
			case BLOCK_TYPE_MALLOC_ALIGN:
				return MEMORY_ERROR_MIX_FREE_ALIGN;
			case BLOCK_TYPE_NEW:
				return MEMORY_ERROR_MIX_DELETE;
			case BLOCK_TYPE_NEW_ARRAY:
				return MEMORY_ERROR_MIX_DELETE_ARRAY;
		}
		return MEMORY_ERROR_MIX_UNKNOWN;
	}
	return MEMORY_ERROR_NONE;
}

/**
*/
void pl::_replaceInfo(const void* pUserAddress, const pl_char*& file, pl_uint32& line, const pl_char*& func)
{
	switch (_checkBlock(pUserAddress, -1, 0))
	{
		case MEMORY_ERROR_HEADER:
		case MEMORY_ERROR_FREE_AGAIN:
		case MEMORY_ERROR_NULL:
		case MEMORY_ERROR_GENERAL:
			return;

		default:
			break;
	}

	const _MemHeader* pMemHeader = reinterpret_cast<const _MemHeader*>(pUserAddress) - 1;
	file = pMemHeader->fileName;
	line = pMemHeader->lineNumber;
	func = pMemHeader->funcName;
	return;
}

/**
*/
void* pl::_getAllocAddress(void* pUserAddress, size_t& size)
{
	if (pUserAddress == NULL)
	{
		return NULL;
	}
	_MemHeader* pMemHeader = (_MemHeader*)pUserAddress - 1;

	size = pMemHeader->blockSize;
	return (pl_uint8*)pMemHeader - pMemHeader->actualAddressOffset;
}

/**
*/
void pl::_formatString(String<1024> &str, const _MemHeader* pHdr, bool bIncludeName /*= true*/)
{
	if (bIncludeName)
	{
		if (pHdr->fileName == NULL)
		{
			str.set(PL_T("<NULL>"));
		}
		else
		{
			str.set(pHdr->fileName);
		}
		str << PL_T('(') << pHdr->lineNumber << PL_T("):");
	}
	else
	{
		str.clear();
	}

	str << PL_T('#') << pHdr->allocationIndex << PL_T("; size: ") << pHdr->blockSize << PL_T("; ptr: 0x") << (pl_uint8*)pHdr + pHdr->actualAddressOffset;
	switch (pHdr->blockType)
	{
		case BLOCK_TYPE_MALLOC:
			str += PL_T("; malloc()");
			break;
		case BLOCK_TYPE_MALLOC_ALIGN:
			str += PL_T("; malloc_align()");
			break;
		case BLOCK_TYPE_NEW:
			str += PL_T("; new");
			break;
		case BLOCK_TYPE_NEW_ARRAY:
			str += PL_T("; new []");
			break;
		case BLOCK_TYPE_STATIC:
			str += PL_T("; malloc_static()");
			break;
		default:
			str += PL_T("; UNKNOWN alocation type");
	}

	MemoryError error = _checkBlock(pHdr + 1, pHdr->blockType, 0);
	if (error != MEMORY_ERROR_NONE)
	{
		str << PL_T("; DAMAGE CODE: ") << (int)error;
	}
	if (pHdr->allocBeforeInit)
	{
		str += PL_T("; Alloc BEFORE INIT");
	}
	return;
}

/**
*/
static size_t pl::_calcDebugSpace(size_t size, pl_uint32 align)
{
	size += (sizeof(_MemHeader) + _memGlobalInfo.footerSize);

	if (align == 0)
	{
		return size;
	}
	return 2*align + size;
}

/**
*/
static void* pl::_getUserAddress(void* pAllocatedAddress, pl_uint32 align)
{
	pAllocatedAddress = (void*)((pl_uint8*)pAllocatedAddress + sizeof(_MemHeader));

	if (align == 0)
	{
		return pAllocatedAddress;
	}

	pl_uint32 alignOffset = (pl_uint32)((pl_intptr)pAllocatedAddress % align);
	if (alignOffset > 0)
	{
		pAllocatedAddress = (void*)((pl_uint8*)pAllocatedAddress + (align - alignOffset));
	}
	return pAllocatedAddress;
}

/**
*/
static void pl::_fillDebugInfo(void* pAllocatedAddress, void* pUserAddress, size_t blockSize, const pl_char* fileName, pl_uint32 lineNumber, const pl_char* func, BlockType blockType)
{
	pl_uint32 i;
	_MemHeader* pMemHeader = reinterpret_cast<_MemHeader*>(pUserAddress) - 1;

	pMemHeader->actualAddressOffset = (pl_uint8*)pMemHeader - (pl_uint8*)pAllocatedAddress;
	pMemHeader->blockSize = (pl_uint32)blockSize;
	pMemHeader->blockType = blockType;
	pMemHeader->fileName = fileName;
	pMemHeader->lineNumber = (pl_uint16)lineNumber;
	pMemHeader->funcName = func;
	pMemHeader->pNextBlock = NULL;
	pMemHeader->pPrevBlock = NULL;
	pMemHeader->threadId = pl::getThreadId();

	pMemHeader->allocBeforeInit = (_memGlobalInfo.pDebugCs == NULL) ? true : false;
	pMemHeader->allocationIndex = ++_memGlobalInfo.curAllocationIndex;
	pMemHeader->internalAllocationFlag = _memGlobalInfo.fInternalAllocations ? 1 : 0;
	pMemHeader->sign1 = _HEADER_SIGN;
	pMemHeader->sign2 = _HEADER_SIGN;

	if (_memGlobalInfo.footerSize > 0)
	{
		memset((pl_uint8*)pUserAddress + blockSize, _FOOTER_SIGN, _memGlobalInfo.footerSize);
	}

	for (i=0; i<_memGlobalInfo.breakIndex.size(); i++)
	{
		if (_memGlobalInfo.curAllocationIndex == _memGlobalInfo.breakIndex[i])
		{
			PL_MEM_ERROR1(fileName, lineNumber, func, pAllocatedAddress, blockSize, MEMORY_ERROR_INDEX_REACHED);
		}
	}
	for (i=0; i<_memGlobalInfo.breakAddress.size(); i++)
	{
		if (_memGlobalInfo.breakAddress[i] >= pAllocatedAddress && _memGlobalInfo.breakAddress[i] <= ((pl_uint8*)pUserAddress + blockSize))
		{
			PL_MEM_ERROR1(fileName, lineNumber, func, pAllocatedAddress, blockSize, MEMORY_ERROR_ADDRESS_REACHED);
		}
	}
	return;
}

/**
*/
static void pl::_linkBlock(void* pUserAddress)
{
	if (_memGlobalInfo.pDebugCs != NULL)
	{
		_memGlobalInfo.pDebugCs->enter();
	}

	_MemHeader* pMemHeader = reinterpret_cast<_MemHeader*>(pUserAddress) - 1;

	PL_ASSERTE(pMemHeader->pNextBlock == NULL, PL_T("Internal DebugMemory error"));
	PL_ASSERTE(pMemHeader->pPrevBlock == NULL, PL_T("Internal DebugMemory error"));

	if (_memGlobalInfo.pFirstBlock == NULL)
	{
		_memGlobalInfo.pFirstBlock = pMemHeader;
	}
	else
	{
		PL_ASSERTE(_memGlobalInfo.pFirstBlock->pPrevBlock == NULL, PL_T("Internal DebugMemory error"));
		pMemHeader->pNextBlock = _memGlobalInfo.pFirstBlock;
		_memGlobalInfo.pFirstBlock->pPrevBlock = pMemHeader;
		_memGlobalInfo.pFirstBlock = pMemHeader;
	}

	_memGlobalInfo.nAllocatedBlocks++;
	_memGlobalInfo.nAllocatedSize += pMemHeader->blockSize;

	if (_memGlobalInfo.pDebugCs != NULL)
	{
		_memGlobalInfo.pDebugCs->leave();
	}
	return;
}

/**
*/
static void pl::_unlinkBlock(void* pUserAddress)
{
	if (_memGlobalInfo.pDebugCs != NULL)
	{
		_memGlobalInfo.pDebugCs->enter();
	}

	_MemHeader* pMemHeader = reinterpret_cast<_MemHeader*>(pUserAddress) - 1;

	if (pMemHeader == _memGlobalInfo.pFirstBlock)
	{
		PL_ASSERTE(_memGlobalInfo.pFirstBlock->pPrevBlock == NULL, PL_T("Internal DebugMemory error"));
		_memGlobalInfo.pFirstBlock = pMemHeader->pNextBlock;
		if (_memGlobalInfo.pFirstBlock)
		{
			_memGlobalInfo.pFirstBlock->pPrevBlock = NULL;
		}
	}
	else
	{
		if (pMemHeader->pNextBlock != NULL)
		{
			pMemHeader->pNextBlock->pPrevBlock = pMemHeader->pPrevBlock;
		}
		if (pMemHeader->pPrevBlock != NULL)
		{
			pMemHeader->pPrevBlock->pNextBlock = pMemHeader->pNextBlock;
		}
		pMemHeader->pPrevBlock = pMemHeader->pNextBlock = NULL;
	}

	_memGlobalInfo.nAllocatedBlocks--;
	_memGlobalInfo.nAllocatedSize -= pMemHeader->blockSize;

	pMemHeader->pPrevBlock = pMemHeader->pNextBlock = NULL;
	if (_memGlobalInfo.pDebugCs != NULL)
	{
		_memGlobalInfo.pDebugCs->leave();
	}
	return;
}

#endif // PL_MEMORY_DEBUG

//
// End of file 'pulsar_memorydbg.cpp'
//
